// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

`include "mainfpga_version.vh"
`include "mainfpga_csr_addr.vh"


module cpu_global_csr
(
    input clk,
    input reset,

    //avmm interface
    input      [31:0] avmm_address,
    input             avmm_write,
    input      [31:0] avmm_writedata,          
    input             avmm_read,     
    output reg [31:0] avmm_readdata,      
    output reg        avmm_readdatavalid, 
    output            avmm_waitrequest,           
    input      [ 3:0] avmm_byteenable,

    //misc signals
    input      [31:0] status_pwr_seq_1,
    input      [31:0] status_pwr_seq_2,
    input      [31:0] status_pwr_seq_3,
    input      [31:0] status_pwr_seq_4,

    input             mcsi_aligned,
    input      [3:0] pwr_err_source,
    input      [3:0]  rstate,
    input             pwr_restart_clear,
    input             force_to_enter_err_clear,
    output            pwr_restart,
    output            force_to_enter_err,
    //error report
    output            invalid_access,
	 input     [31:0]  ADRcounter1,
	 input     [31:0]  ADRcounter2,
     input             iGLBRST_STATUS,
    input               iGLBRST_CPU,
    input               iGLBRST_BMC,
    output            oGLBRST_HOLD_OFF_EN,
    output            oGLBRST_HOLD_OFF,
    output            oNMI_SEL_CPU_eSPI,
    input      [31:0] iSEL_MUL_PWRSEQ_MEASURE
);

wire [15:0] addr_local; //Only 16 bits out of 32 bits avmm_address supported
reg  [31:0] cpu_global_reg [`CPU_GLOBAL_MAX_ADDR-1:0]; //local register
reg  [31:0] illegal_write;
wire pwr_restart_clear_posedge;
wire force_to_enter_err_clear_posedge;

reg         invalid_read;
reg         invalid_write;
assign avmm_waitrequest = 1'b0; //Fix wait request to 1'b0 as no wait needed

assign addr_local = avmm_address[15:0];

assign invalid_access   =  invalid_write || invalid_read;

//invalid_read, using combinational logic to report error
always @ ( * ) begin
    if ( reset ) begin
        invalid_read       = 1'b0;
    end else begin
        if ( avmm_read && !( |avmm_address[31:16]) && (&avmm_byteenable)) begin
            case (addr_local)
                 `MAIN_FPGA_VERSION_ADDR    : begin 
					     invalid_read           =   1'b0;
                 end
                 `MAIN_REV_CTRL_ADDR        : begin
					     invalid_read           =   1'b0;
                 end
                 `MAIN_BUILD_TIME_ADDR      : begin
                    invalid_read           =   1'b0;
                 end						  
                 `MAIN_BUILD_CATEGORY_ADDR  : begin
					     invalid_read           =   1'b0;
                 end
                 `MAIN_DEVICE_LOCATION_ADDR : begin
					     invalid_read           =   1'b0;
                 end
                 `MAIN_STATUS_ADDR          : begin
					     invalid_read           =   1'b0;
                 end
                 `MAIN_SCRATCH_ADDR         : begin
                    invalid_read           =   1'b0;
                 end  
                 `CPU_PWR_SEQUENCE_STATUS_ADDR : begin
					     invalid_read           =   1'b0;
                 end
                 `CPU_PWR_SEQUENCE_CONTROL_ADDR: begin
					     invalid_read           =   1'b0;
                 end
					  `CPU_ADR_COUNTER1_ADDR: begin
					     invalid_read           =   1'b0;
                 end
					  `CPU_ADR_COUNTER2_ADDR: begin
					     invalid_read           =   1'b0;
                 end
					  `CPU_GLBRST_FLOW_CONTROL_ADDR: begin
					     invalid_read           =   1'b0;
                 end

                 `CPU_NMI_SELECTOR_IRQ: begin
                 invalid_read           =   1'b0;
                 end

                 `CPU_DEBUG_PWR_SEQUENCE_1: begin
                 invalid_read           =   1'b0;
                 end

                 `CPU_DEBUG_PWR_SEQUENCE_2: begin
                 invalid_read           =   1'b0;
                 end
                 
                 `CPU_DEBUG_PWR_SEQUENCE_3: begin
                 invalid_read           =   1'b0;
                 end                             
                 
                 `CPU_DEBUG_PWR_SEQUENCE_4: begin
                 invalid_read           =   1'b0;
                 end

                 `CPU_DEBUG_PWR_SEQUENCE_MULT_SEL: begin
                 invalid_read           =   1'b0;
                 end

                 default                       : begin
					     invalid_read           =   1'b1;
                 end
            endcase
        end
		  else begin
		     invalid_read                    =   1'b0;
        end
    end
end

//read registers
always @ ( posedge clk ) begin
    if ( reset ) begin
        avmm_readdata     <= 32'h0;
    end else begin
        if ( avmm_read && !( |avmm_address[31:16]) && (&avmm_byteenable)) begin
            case (addr_local)
                 `MAIN_FPGA_VERSION_ADDR    : begin 
					     avmm_readdata         <=   {8'b0, `MAIN_MAJOR_VERSION, 8'b0, `MAIN_MINOR_VERSION};
                 end
                 `MAIN_REV_CTRL_ADDR        : begin
					     avmm_readdata         <=   `MAIN_GIT_HASH_DEF;
                 end
                 `MAIN_BUILD_TIME_ADDR      : begin
                         avmm_readdata         <=   {`MAIN_BUILD_YEAR_DEF,
                                                     `MAIN_BUILD_MONTH_DEF,
                                                     `MAIN_BUILD_DAY_DEF,
                                                     `MAIN_BUILD_HOUR_DEF,
                                                     `MAIN_BUILD_MINUTE_DEF };
                 end						  
                 `MAIN_BUILD_CATEGORY_ADDR  : begin
					     avmm_readdata         <=   {31'h0, `MAIN_BUILD_CATEGORY_DEF};
                 end
                 `MAIN_DEVICE_LOCATION_ADDR : begin
					     avmm_readdata         <=   {30'h0, `MAIN_DEVICE_LOCTAION_DEF};
                 end
                 `MAIN_STATUS_ADDR          : begin
					     avmm_readdata         <=   {31'h0, mcsi_aligned};
                 end
                 `MAIN_SCRATCH_ADDR         : begin
					     avmm_readdata         <=   cpu_global_reg[`MAIN_SCRATCH_ADDR / 4];
                 end  
                 `CPU_PWR_SEQUENCE_STATUS_ADDR : begin
					     avmm_readdata         <=   {24'h0,pwr_err_source,rstate};
                 end
                 `CPU_PWR_SEQUENCE_CONTROL_ADDR: begin
					     avmm_readdata         <=   cpu_global_reg[`CPU_PWR_SEQUENCE_CONTROL_ADDR / 4];
                 end
					  `CPU_ADR_COUNTER1_ADDR: begin
					     avmm_readdata         <=   ADRcounter1;
                 end
					  `CPU_ADR_COUNTER2_ADDR: begin
					     avmm_readdata         <=   ADRcounter2;
                 end
					  `CPU_GLBRST_FLOW_CONTROL_ADDR: begin
					     avmm_readdata         <=   { cpu_global_reg[`CPU_GLBRST_FLOW_CONTROL_ADDR / 4][31:5],
                                                      iGLBRST_CPU,
                                                      iGLBRST_BMC,
                                                      cpu_global_reg[`CPU_GLBRST_FLOW_CONTROL_ADDR / 4][2:1],
                                                      iGLBRST_STATUS
                                                    };
                 end
                 
                 
                 `CPU_NMI_SELECTOR_IRQ: begin
                 avmm_readdata         <= cpu_global_reg[ `CPU_NMI_SELECTOR_IRQ / 4];
                 end    

					  
                 `CPU_DEBUG_PWR_SEQUENCE_1: begin
                 avmm_readdata         <=  status_pwr_seq_1;
                 end

                 `CPU_DEBUG_PWR_SEQUENCE_2: begin
                 avmm_readdata         <=  status_pwr_seq_2;
                 end
                 
                 
                 `CPU_DEBUG_PWR_SEQUENCE_3: begin
                 avmm_readdata         <=  status_pwr_seq_3;
                 end
                 
                 
                 `CPU_DEBUG_PWR_SEQUENCE_4: begin
                 avmm_readdata         <=  status_pwr_seq_4;
                 end

                 `CPU_DEBUG_PWR_SEQUENCE_MULT_SEL: begin
                 avmm_readdata  <= iSEL_MUL_PWRSEQ_MEASURE;
                 end
 
                 default                       : begin
					     avmm_readdata         <=   32'hFFFF_FFFF; //reture bad addr
                 end
            endcase
        end
    end
end

//readvalid
always @ ( posedge clk ) begin
    if ( reset ) begin
        avmm_readdatavalid <= 1'b0;
    end else begin
        if ( avmm_read && (&avmm_byteenable) && (!invalid_read)) begin
            avmm_readdatavalid <= 1'b1; //readvalid asserts one clock cycle after read assetted.
        end else begin
            avmm_readdatavalid <= 1'b0;
        end
    end
end

//invalid_write, using combinational logic to report error
always @ ( * ) begin
    if ( reset ) begin
		  invalid_write                 = 1'b0;
    end else begin
        if ( avmm_write && !( |avmm_address[31:16]) && (&avmm_byteenable)) begin
            case (addr_local)
                `MAIN_SCRATCH_ADDR             : begin
		              invalid_write                                        =   1'b0;
                 end
		          `CPU_PWR_SEQUENCE_CONTROL_ADDR    : begin
                    if (!(|avmm_writedata[31:2]))   begin
		                 invalid_write                                     =   1'b0;
                    end
		              else begin
		                 invalid_write                                     =   1'b1;
                    end
                 end
		          `CPU_GLBRST_FLOW_CONTROL_ADDR    : begin
                    if (!(|{avmm_writedata[31:3], avmm_writedata[0]})) begin
		                 invalid_write                                     =   1'b0;
                    end
		              else begin
		                 invalid_write                                     =   1'b1;
                    end
                 end

                 `CPU_NMI_SELECTOR_IRQ   : begin
                 if (!(|avmm_writedata[31:1])) begin
                      invalid_write                                     =   1'b0;
                 end
                   else begin
                      invalid_write                                     =   1'b1;
                 end
              end


                default                           : begin
		             invalid_write                                         =   1'b1;
                end
            endcase
        end
	     else begin
	        invalid_write              =   1'b0;
        end
    end
end

//write registers
always @ ( posedge clk ) begin
    if ( reset ) begin
        cpu_global_reg[`MAIN_SCRATCH_ADDR/4]           <= 32'h0;
        cpu_global_reg[`CPU_PWR_SEQUENCE_CONTROL_ADDR/4]  <= 32'h0;
        cpu_global_reg[`CPU_NMI_SELECTOR_IRQ/4]  <= 32'h0;
    end if (pwr_restart_clear_posedge) begin
        cpu_global_reg[`CPU_PWR_SEQUENCE_CONTROL_ADDR/4][0] <= 1'b0;
    end if (force_to_enter_err_clear_posedge) begin
        cpu_global_reg[`CPU_PWR_SEQUENCE_CONTROL_ADDR/4][1] <= 1'b0;
    end else begin
        if ( avmm_write && !( |avmm_address[31:16]) && (&avmm_byteenable)) begin
            case (addr_local)
                `MAIN_SCRATCH_ADDR              : begin
		             cpu_global_reg[`MAIN_SCRATCH_ADDR/4]              <=   avmm_writedata;
                end
		          `CPU_PWR_SEQUENCE_CONTROL_ADDR    : begin
                   if (!(pwr_restart_clear_posedge || force_to_enter_err_clear_posedge) && !(|avmm_writedata[31:2]))    begin
		                cpu_global_reg[`CPU_PWR_SEQUENCE_CONTROL_ADDR/4]  <=   avmm_writedata;
                   end
		             else begin
		                illegal_write                                     <=   avmm_writedata;
                   end
                end
		          `CPU_GLBRST_FLOW_CONTROL_ADDR     : begin
                   if (!(|{avmm_writedata[31:3], avmm_writedata[0]})) begin
		                cpu_global_reg[`CPU_GLBRST_FLOW_CONTROL_ADDR/4]   <=   avmm_writedata;
                   end
		             else begin
		                illegal_write                                     <=   avmm_writedata;
                   end
                end

                `CPU_NMI_SELECTOR_IRQ     : begin
                   if (!(|avmm_writedata[31:1])) begin
                     cpu_global_reg[`CPU_NMI_SELECTOR_IRQ /4]   <=   avmm_writedata;
                   end
                  else begin
                     illegal_write                                     <=   avmm_writedata;
                   end
                end


              
                  default                           : begin
		             illegal_write                                        <=   avmm_writedata; //reture bad addr
                end
            endcase
        end
	 end
end

assign pwr_restart = cpu_global_reg[`CPU_PWR_SEQUENCE_CONTROL_ADDR/4][0];
assign force_to_enter_err = cpu_global_reg[`CPU_PWR_SEQUENCE_CONTROL_ADDR/4][1];

assign oGLBRST_HOLD_OFF_EN = cpu_global_reg [`CPU_GLBRST_FLOW_CONTROL_ADDR/4][1];
assign oGLBRST_HOLD_OFF    = cpu_global_reg [`CPU_GLBRST_FLOW_CONTROL_ADDR/4][2];
assign oNMI_SEL_CPU_eSPI    = cpu_global_reg [`CPU_NMI_SELECTOR_IRQ/4][0];

EdgeDetector #                                          //Parameterizable Edge Detector
(
    .EDGE (1)                                           //Defines Positive Edge (1) or Negative Edge (0)
)  PWR_RESTART_CLEAR_POSEDGE
(
    .iClk(clk),                                         //Clock Input        
    .iRst(reset),                                       //Asynchronous Reset Input      
    .iSignal(pwr_restart_clear),                        //Monitored Input Signal      
    .oEdgeDetected(pwr_restart_clear_posedge)           //Edge Detected Flag
);

EdgeDetector #                                          //Parameterizable Edge Detector
(
    .EDGE (1)                                           //Defines Positive Edge (1) or Negative Edge (0)
)  FORCE_TO_ENTER_ERR_CLEAR_POSEDGE
(
    .iClk(clk),                                         //Clock Input        
    .iRst(reset),                                       //Asynchronous Reset Input      
    .iSignal(force_to_enter_err_clear),                 //Monitored Input Signal      
    .oEdgeDetected(force_to_enter_err_clear_posedge)    //Edge Detected Flag
);

endmodule    